package roborally;
import static org.junit.Assert.*;


import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import roborally.Exceptions.IllegalAmountException;

public class RobotTest {
	
	private static Robot energylessRobot;

	/**
	 * 		   Set up an immutable test Robot. This robot will act as a fixed point to which other robots are going to move.
	 * 
	 * @post   The variable energylessRobot references a new Robot with 0 energy,
	 * 		   located at (5,5) facing up (0,North,to the top).
	 */
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
		energylessRobot = new Robot(5, 5, 0, 0);
	}

	private Robot robotTopLeftSufficientEnergy, robotTopRightSufficientEnergy, robotBottomLeftSufficientEnergy,robotBottomrightSufficientEnergy, //Normal Cases
				  robotToTopSufficientEnergy,	robotToBottomSufficientEnergy, robotToLeftSufficientEnergy,	   robotToRightSufficientEnergy;	 //Special Cases

	/**
	 * Set up a mutable test fixture.
	 * 
	 * @post   The variable robotTopLeftSufficientEnergy references a new robot
	 * 			with 20000 energy, located at (1,1) facing up.
	 *        
	 * @post   The variable robotTopRightSufficientEnergy references a new robot
	 * 			with 20000 energy, located at (10,1) facing up.
	 * 
	 * @post   The variable robotBottomLeftSufficientEnergy references a new robot
	 *  		with 20000 energy, located at (1,10) facing up.
	 * 
	 * @post   The variable robotBottomrightSufficientEnergy references a new robot
	 *  		with 20000 energy, located at (10,10) facing up.
	 * 
	 * @post   The variable robotToTopSufficientEnergy references a new robot
	 *  		with 20000 energy, located at (5,1) facing up.
	 * 
	 * @post   The variable robotToBottomSufficientEnergy references a new robot
	 *  		with 20000 energy, located at (5,10) facing up.
	 * 
	 * @post   The variable robotToLeftSufficientEnergy references a new robot
	 *  		with 20000 energy, located at (1,5) facing up.
	 * 
	 * @post   The variable robotToRightSufficientEnergy references a new robot
	 *  		with 20000 energy, located at (10,5) facing up.
	 */
	@Before
	public void setUp() throws Exception {
		robotTopLeftSufficientEnergy = new Robot(1,1,0,20000);
		robotTopRightSufficientEnergy = new Robot(10,1,0,20000);
		robotBottomLeftSufficientEnergy = new Robot(1,10,0,20000);
		robotBottomrightSufficientEnergy = new Robot(10,10,0,20000);
		robotToTopSufficientEnergy = new Robot(5,1,0,20000);
		robotToBottomSufficientEnergy = new Robot(5,10,0,20000);
		robotToLeftSufficientEnergy = new Robot(1,5,0,20000);
		robotToRightSufficientEnergy = new Robot(10,5,0,20000);
	}
	
	@Test
	public final void testConstructor() {
		Robot newRobot = new Robot(3,6,2,1500);
		assertNotNull(newRobot);
		assertEquals(3, newRobot.getX());
		assertEquals(6, newRobot.getY());
		assertEquals(2, newRobot.getOrientation());
		assertEquals(1500, newRobot.getEnergy(),0.1);
	}
	
	@Test
	public final void testValidationMethods() {
		assertEquals(true, Robot.isValidXCoordinate(3));
		assertEquals(true, Robot.isValidYCoordinate(6));
		assertEquals(true, Robot.isValidOrientation(2));
		assertEquals(true, Robot.isValidEnergyAmount(1500,20000));
		assertEquals(true, Robot.isValidMaxEnergyAmount(20000));
		assertEquals(true, Robot.isAddableEnergyAmount(500, 20000, 1000));
		
		assertEquals(false, Robot.isValidXCoordinate(-5));
		assertEquals(false, Robot.isValidYCoordinate(-10));
		assertEquals(false, Robot.isValidOrientation(5));
		assertEquals(false, Robot.isValidEnergyAmount(500000,20000));
		assertEquals(false, Robot.isValidEnergyAmount(-5000,20000));
		assertEquals(false, Robot.isValidMaxEnergyAmount(-1000));
		assertEquals(false, Robot.isAddableEnergyAmount(50000000, 20000, 1000));
	}
	
	@Test
	public final void testGetEnergyMethods() {
		Robot testRobot = new Robot(10,20,0,1000);
		assertEquals(1000, testRobot.getEnergy(),0.1);
		assertEquals(20000, testRobot.getMaxEnergy(),0.1);
		assertEquals(1/20, testRobot.getFractionEnergy(),0.1);
	}
	
	@Test
	public final void testMoveSufficientEnergy() {
		//move positive X
		Robot testRobot = new Robot(10,20,1,1000);
		testRobot.move();
		assertEquals(11, testRobot.getX());
		
		//move positive Y
		Robot testRobot2 = new Robot(10,20,2,1000);
		testRobot2.move();
		assertEquals(21, testRobot2.getY());
		
		//move negative X
		Robot testRobot3 = new Robot(10,20,3,1000);
		testRobot3.move();
		assertEquals(9, testRobot3.getX());
		
		//move negative Y
		Robot testRobot4 = new Robot(10,20,0,1000);
		testRobot4.move();
		assertEquals(19, testRobot4.getY());
	}
	
	@Test
	public final void testMoveInsufficientEnergy() {
		//Try to move positive X
		Robot testRobot = new Robot(10,20,1,0);
		testRobot.move();
		assertEquals(10, testRobot.getX());
		
		//Try to move positive Y
		Robot testRobot2 = new Robot(10,20,2,0);
		testRobot2.move();
		assertEquals(20, testRobot2.getY());
	}
	
	@Test (expected = IllegalAmountException.class)
	public final void testSubtractXException() throws IllegalAmountException {		
		Robot testRobot = new Robot(0,20,3,5000);
		testRobot.move();
	}
	
	@Test (expected = IllegalAmountException.class)
	public final void testSubtractYException() throws IllegalAmountException {
		Robot testRobot = new Robot(0,0,0,5000);
		testRobot.move();
	}
	
	
	
	@Test
	public final void testTurningMethods() {
		Robot testRobot = new Robot(10,20,1,0);
		testRobot.turnClockwise();
		assertEquals(2, testRobot.getOrientation());
		testRobot.turnClockwise(2);
		assertEquals(0, testRobot.getOrientation());
		testRobot.turnAntiClockwise();
		assertEquals(3, testRobot.getOrientation());
		testRobot.turnAntiClockwise(3);
		assertEquals(0, testRobot.getOrientation());
	}
	
	@Test
	public final void testRecharge() {
		Robot testRobot = new Robot(10,20,1,0);
		assertEquals(0, testRobot.getEnergy(),0.1);
		testRobot.recharge(500);
		assertEquals(500, testRobot.getEnergy(),0.1);
	}
	
	@Test
	public final void testIsGetEnergyRequiredToReachAndMoveNextTo16Plus() {
		Robot testRobot = new Robot(10,20,1,0);
		assertEquals(1, testRobot.isGetEnergyRequiredToReachAndMoveNextTo16Plus());
	}
	
	@Test
	public final void testGetEnergyRequiredToReachBottomRight1() {
		Robot testRobot = new Robot(0,0,1,20000);
		assertEquals(5100,testRobot.getEnergyRequiredToReach(5, 5),0.1);
	}

	@Test
	public final void testGetEnergyRequiredToReachBottomRight2() {
		Robot testRobot = new Robot(0,0,2,20000);
		assertEquals(5100,testRobot.getEnergyRequiredToReach(5, 5),0.1);
	}
	@Test
	public final void testGetEnergyRequiredToReachTopRight() {
		Robot testRobot = new Robot(0,5,3,20000);
		assertEquals(5200,testRobot.getEnergyRequiredToReach(5, 0),0.1);
	}
	
	@Test
	public final void testGetEnergyRequiredToReachTopLeft() {
		Robot testRobot = new Robot(5,5,0,20000);
		assertEquals(5100,testRobot.getEnergyRequiredToReach(0, 0),0.1);
	}
	
	@Test
	public final void testGetEnergyRequiredToReachBottomLeft() {
		Robot testRobot = new Robot(5,5,0,20000);
		assertEquals(5200,testRobot.getEnergyRequiredToReach(0, 10),0.1);
	}

	@Test
	public final void testGetEnergyRequiredToReachToTop() {
		Robot testRobot = new Robot(20,20,2,20000);
		assertEquals(2700,testRobot.getEnergyRequiredToReach(20, 15),0.1);
	}

	@Test
	public final void testGetEnergyRequiredToReachToBottom() {
		Robot testRobot = new Robot(20,20,3,20000);
		assertEquals(4100,testRobot.getEnergyRequiredToReach(20, 12),0.1);
	}

	@Test
	public final void testGetEnergyRequiredToReachToLeft() {
		Robot testRobot = new Robot(20,20,1,20000);
		assertEquals(3200,testRobot.getEnergyRequiredToReach(14, 20),0.1);
	}

	@Test
	public final void testGetEnergyRequiredToReachToRight() {
		Robot testRobot = new Robot(20,20,0,20000);
		assertEquals(3100,testRobot.getEnergyRequiredToReach(26, 20),0.1);
	}

	@Test
	public final void testGetEnergyRequiredToReachOnSamePlace() {
		Robot testRobot = new Robot(20,20,3,20000);
		assertEquals(0,testRobot.getEnergyRequiredToReach(20, 20),0.1);
	}
	
	//THE FOLLOWING IS A MASSIVE TEST LIST FOR MOVE NEXT TO, GOING THROUGH EACH POSSIBLE CASE.
	//EVERY TIME THE METHODE MOVENEXTTO() IS CALLED TWICE, SWITCHING THE ROBOTS PLACES IN THE ARGUMENTS
	
	//THIS LIST GOES THROUGH ALL POSSIBLE CASES AND ONLY 1 OF THE ROBOTS CAN MOVE
	@Test
	public final void testMoveNextToCaseBottomRight1CanMove() {
		Robot testRobot = new Robot(5,5,1,0);
		Robot testRobot2 = new Robot(0,0,1,20000);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
	
		Robot testRobot3 = new Robot(5,5,1,0);
		Robot testRobot4 = new Robot(0,0,1,20000);
		testRobot.moveNextTo(testRobot4, testRobot3);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));}
	
	@Test
	public final void testMoveNextToCaseTopLeft1CanMove() {
		Robot testRobot = new Robot(5,5,1,20000);
		Robot testRobot2 = new Robot(0,0,1,0);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		
		Robot testRobot3 = new Robot(5,5,1,20000);
		Robot testRobot4 = new Robot(0,0,1,0);
		testRobot.moveNextTo(testRobot4, testRobot3);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
	}
	
	@Test
	public final void testMoveNextToCaseTopRight1CanMove() {
		Robot testRobot = new Robot(5,5,1,20000);
		Robot testRobot2 = new Robot(10,1,1,0);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		
		Robot testRobot3 = new Robot(5,5,1,20000);
		Robot testRobot4 = new Robot(10,1,1,0);
		testRobot.moveNextTo(testRobot4, testRobot3);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
	}
	
	@Test
	public final void testMoveNextToCaseBottomLeft1CanMove() {
		Robot testRobot = new Robot(5,5,1,0);
		Robot testRobot2 = new Robot(10,1,1,20000);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		
		Robot testRobot3 = new Robot(5,5,1,0);
		Robot testRobot4 = new Robot(10,1,1,20000);
		testRobot.moveNextTo(testRobot4, testRobot3);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		}
	
	@Test
	public final void testMoveNextToCaseToTop1CanMove() {
		Robot testRobot = new Robot(5,5,1,20000);
		Robot testRobot2 = new Robot(5,1,1,0);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		
		Robot testRobot3 = new Robot(5,5,1,20000);
		Robot testRobot4 = new Robot(5,1,1,0);
		testRobot.moveNextTo(testRobot4, testRobot3);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));	
	}
	
	@Test
	public final void testMoveNextToCaseToBottom1CanMove() {
		Robot testRobot = new Robot(5,5,1,0);
		Robot testRobot2 = new Robot(5,1,1,20000);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		
		Robot testRobot3 = new Robot(5,5,1,0);
		Robot testRobot4 = new Robot(5,1,1,20000);
		testRobot.moveNextTo(testRobot4, testRobot3);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
	}

	@Test
	public final void testMoveNextToCaseToLeft1CanMove() {
		Robot testRobot = new Robot(1,1,1,0);
		Robot testRobot2 = new Robot(5,1,1,20000);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		
		Robot testRobot3 = new Robot(1,1,1,0);
		Robot testRobot4 = new Robot(5,1,1,20000);
		testRobot.moveNextTo(testRobot4, testRobot3);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		}
	
	@Test
	public final void testMoveNextToCaseToRight1CanMove() {
		Robot testRobot = new Robot(1,1,1,20000);
		Robot testRobot2 = new Robot(5,1,1,0);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		
		Robot testRobot3 = new Robot(1,1,1,20000);
		Robot testRobot4 = new Robot(5,1,1,0);
		testRobot.moveNextTo(testRobot4, testRobot3);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
	}
	
	@Test
	public final void testMoveNextToCaseAlreadyNextToEachOther1CanMove() {
		Robot testRobot = new Robot(1,2,1,20000);
		Robot testRobot2 = new Robot(1,1,1,0);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		
		Robot testRobot3 = new Robot(1,2,1,20000);
		Robot testRobot4 = new Robot(1,1,1,0);
		testRobot.moveNextTo(testRobot4, testRobot3);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
	}
	
	@Test
	public final void testMoveNextToCaseOnTopOfEachOther1CanMove() {
		Robot testRobot = new Robot(1,1,1,20000);
		Robot testRobot2 = new Robot(1,1,1,0);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		
		Robot testRobot3 = new Robot(1,1,1,20000);
		Robot testRobot4 = new Robot(1,1,1,0);
		testRobot.moveNextTo(testRobot4, testRobot3);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		}
	
	//THIS LIST GOES THROUGH ALL THE CASES AND BOTH ROBOTS CAN MOVE.
	
	@Test
	public final void testMoveNextToCaseBottomRightAndTopLeft2CanMove() {
		Robot testRobot = new Robot(5,5,1,20000);
		Robot testRobot2 = new Robot(0,0,1,20000);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		
		Robot testRobot3 = new Robot(5,5,1,20000);
		Robot testRobot4 = new Robot(0,0,1,20000);
		testRobot.moveNextTo(testRobot4, testRobot3);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		}
	
	@Test
	public final void testMoveNextToCaseTopRightAndBottomLeft2CanMove() {
		Robot testRobot = new Robot(5,5,1,20000);
		Robot testRobot2 = new Robot(10,1,1,20000);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		
		Robot testRobot3 = new Robot(5,5,1,20000);
		Robot testRobot4 = new Robot(10,1,1,20000);
		testRobot.moveNextTo(testRobot4, testRobot3);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		}
	
	@Test
	public final void testMoveNextToCaseToTopAndToBottom2CanMove() {
		Robot testRobot = new Robot(5,5,1,20000);
		Robot testRobot2 = new Robot(5,1,1,20000);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		
		Robot testRobot3 = new Robot(5,5,1,20000);
		Robot testRobot4 = new Robot(5,1,1,20000);
		testRobot.moveNextTo(testRobot4, testRobot3);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		}


	@Test
	public final void testMoveNextToCaseToLeftAndToRight2CanMove() {
		Robot testRobot = new Robot(1,1,1,20000);
		Robot testRobot2 = new Robot(5,1,1,20000);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		
		Robot testRobot3 = new Robot(1,1,1,20000);
		Robot testRobot4 = new Robot(5,1,1,20000);
		testRobot.moveNextTo(testRobot4, testRobot3);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		}

	
	@Test
	public final void testMoveNextToCaseAlreadyNextToEachOther2CanMove() {
		Robot testRobot = new Robot(1,2,1,20000);
		Robot testRobot2 = new Robot(1,1,1,20000);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		
		Robot testRobot3 = new Robot(1,2,1,20000);
		Robot testRobot4 = new Robot(1,1,1,20000);
		testRobot.moveNextTo(testRobot4, testRobot3);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		}
	
	@Test
	public final void testMoveNextToCaseOnTopOfEachOther2CanMove() {
		Robot testRobot = new Robot(1,1,1,20000);
		Robot testRobot2 = new Robot(1,1,1,20000);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		
		Robot testRobot3 = new Robot(1,1,1,20000);
		Robot testRobot4 = new Robot(1,1,1,20000);
		testRobot.moveNextTo(testRobot4, testRobot3);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
		}
	
	//TESTING SPECIFIC SPECIAL CASES OF POSITIONING AND ORIENTATION TO COVER ALL POSSIBILITIES
	
	@Test
	public final void testMoveNextToCaseRobot2IsMoreFavorableTopRight() {
		Robot testRobot = new Robot(5,5,3,20000);
		Robot testRobot2 = new Robot(10,1,2,20000);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
	}
	
	@Test
	public final void testMoveNextToCaseRobot2IsMoreFavorableBottomRight() {
		Robot testRobot = new Robot(5,5,3,20000);
		Robot testRobot2 = new Robot(10,10,3,20000);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
	}
	
	@Test
	public final void testMoveNextToCaseRobot2IsMoreFavorableToBottom() {
		Robot testRobot = new Robot(5,5,0,20000);
		Robot testRobot2 = new Robot(5,10,3,20000);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
	}
	
	@Test
	public final void testMoveNextToCaseRobot2IsMoreFavorableToRight() {
		Robot testRobot = new Robot(5,5,3,20000);
		Robot testRobot2 = new Robot(10,5,3,20000);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
	}
	
	@Test
	public final void testMoveNextToCaseBothCanMove3rdFavoriteOrientation1() {
		Robot testRobot = new Robot(5,5,0,20000);
		Robot testRobot2 = new Robot(10,10,2,20000);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
	}
	
	@Test
	public final void testMoveNextToCaseBothCanMove3rdFavoriteOrientation2() {
		Robot testRobot = new Robot(5,5,3,20000);
		Robot testRobot2 = new Robot(10,10,2,20000);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
	}
	
	@Test
	public final void testMoveNextToCaseBothCanMove3rdFavoriteOrientationSpecialCase1() {
		Robot testRobot = new Robot(5,5,3,20000);
		Robot testRobot2 = new Robot(5,10,2,20000);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
	}
	
	@Test
	public final void testMoveNextToCaseBothCanMove3rdFavoriteOrientationSpecialCase2() {
		Robot testRobot = new Robot(5,5,0,20000);
		Robot testRobot2 = new Robot(5,10,2,20000);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
	}
	
	@Test
	public final void testMoveNextToCaseRobot1isMoveFavorableButInsufficientEnergy() {
		Robot testRobot = new Robot(5,5,0,500);
		Robot testRobot2 = new Robot(10,10,1,20000);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
	}
	
	@Test
	public final void testMoveNextToCaseRobot1isMoveFavorableButInsufficientEnergySpecialCase() {
		Robot testRobot = new Robot(5,5,0,500);
		Robot testRobot2 = new Robot(5,10,1,20000);
		testRobot.moveNextTo(testRobot, testRobot2);
		assertTrue(Robot.standNextToEachOther(testRobot,testRobot2));
	}


}
